/**
 * @File Name: sqlconnpool.cpp
 * @brief  SqlConnPool 类的实现
 * @Author : beyon_sir email:caoboyang.chn@gmail.com
 * @Version : 1.0
 * @Create Date : 2022-11-21
 * 
 */
#include "sqlconnpool.h"
using namespace std;

SqlConnPool::SqlConnPool() {
    useCount_ = 0;
    freeCount_ = 0;
}

SqlConnPool* SqlConnPool::Instance() {
    static SqlConnPool connPool;    // 创建一个静态化SqlConnPool实例
    return &connPool;
}

void SqlConnPool::Init(const char* host, int port,
            const char* user,const char* pwd, const char* dbName,
            int connSize = 10) {
    assert(connSize > 0);
    // 对所有的数据库连接初始化
    for (int i = 0; i < connSize; i++) {
        MYSQL *sql = nullptr;
        sql = mysql_init(sql);
        if (!sql) {
            LOG_ERROR("MySql init error!");
            assert(sql);
        }
        // 连接数据库
        sql = mysql_real_connect(sql, host,                     
                                 user, pwd,
                                 dbName, port, nullptr, 0);
        if (!sql) LOG_ERROR("MySql Connect error!");
        // 将新创建的连接push到连接队列中
        connQue_.push(sql);
    }
    MAX_CONN_ = connSize; // 最大连接数
    sem_init(&semId_, 0, MAX_CONN_); // 初始化信号量 为10
}

/// @brief 获取数据库连接
/// @return 
MYSQL* SqlConnPool::GetConn() {
    MYSQL *sql = nullptr;
    // 数据库连接池如果为空 说明连接已满
    if(connQue_.empty()){
        LOG_WARN("SqlConnPool busy!");
        return nullptr;
    }
    // 信号量sem_wait 如果semId_ = 0 就阻塞等待; 否则semId_-1 继续向下执行逻辑
    sem_wait(&semId_);
    {
        // 访问临界资源 connQue_ 先上锁
        // lock_guard: 作用域锁类型。lock_guard控制互斥锁在一个范围内的所有权，在析构函数中释放所有权。
        lock_guard<mutex> locker(mtx_); 
        sql = connQue_.front();
        connQue_.pop();
    }
    return sql;
}

/// @brief 释放连接
/// @param sql 
void SqlConnPool::FreeConn(MYSQL* sql) {
    assert(sql);
    // 访问临界资源 connQue_ 先上锁
    lock_guard<mutex> locker(mtx_);
    connQue_.push(sql);
    // 信号量 + 1
    sem_post(&semId_);
}

/// @brief 关闭数据库连接池
void SqlConnPool::ClosePool() {
    lock_guard<mutex> locker(mtx_);
    while(!connQue_.empty()) {
        auto item = connQue_.front();
        connQue_.pop();
        mysql_close(item);
    }
    // 把MySQL整体资源关闭
    mysql_library_end();        
}

/// @brief 获取可用的连接数量
/// @return 
int SqlConnPool::GetFreeConnCount() {
    // 访问临界资源 connQue_ 先上锁
    lock_guard<mutex> locker(mtx_);
    return connQue_.size();
}

SqlConnPool::~SqlConnPool() {
    ClosePool();
}
